/*
 * Copyright (C) 2018 The Asiainfo-Ability Authors
 *
 *      Licensed under the Apache License, Version 2.0 (the "License");
 *      you may not use this file except in compliance with the License.
 *      You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *      Unless required by applicable law or agreed to in writing, software
 *      distributed under the License is distributed on an "AS IS" BASIS,
 *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *      See the License for the specific language governing permissions and
 *      limitations under the License.
 */

package org.asiainfo.ability.manager.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.asiainfo.ability.base.utils.ExceptionUtils;
import org.asiainfo.ability.base.utils.SeqUtils;
import org.asiainfo.ability.base.utils.Sequence;
import org.asiainfo.ability.base.utils.StringUtils;
import org.asiainfo.ability.gateway.dao.ServiceDao;
import org.asiainfo.ability.gateway.model.Instance;
import org.asiainfo.ability.gateway.model.Machine;
import org.asiainfo.ability.gateway.model.MicroService;
import org.asiainfo.ability.gateway.vo.Page;
import org.asiainfo.ability.manager.dao.PackageDao;
import org.asiainfo.ability.manager.model.JarPackage;
import org.asiainfo.ability.node.utils.HttpClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @Author: visen
 * @Date: 2018/3/11
 * @Description:
 */
@Component
public class PackageService {

    public static final String REPOPATH = System.getProperty("user.dir") + File.separator + "repos" + File.separator;
    private static final Logger logger = LogManager.getLogger(PackageService.class);
    private static final YAMLMapper yamlMapper = new YAMLMapper();
    private static final String SERVICENAME = "spring.application.name";

    private static final String BRANCH = "eureka.instance.metadataMap.branch";
    private static final String APPLICATION_YML = "application.yml";
    private static final Sequence seqUtils = SeqUtils.newInstance();
    @Autowired
    private SeqService seqService;

    @Autowired
    private AgentService agentService;

    @Autowired
    private PackageDao packageDao;

    @Autowired
    private ServiceDao serviceDao;


    @PostConstruct
    private void init() {
    }

    ThreadPoolExecutor packageExecutor = new ThreadPoolExecutor(2, 8, 600L
            , TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(128));
    ThreadPoolExecutor instanceExecutor = new ThreadPoolExecutor(4, 128, 600L
            , TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2048));

    //@Scheduled(fixedDelay = 10000)
    public void commitRepo() {
        List<JarPackage> jarPackages = packageDao.listChanged();
        for (final JarPackage jarPackage : jarPackages) {
            packageExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    Map<String, List<Instance>> groups = new LinkedHashMap<>();
                    List<Instance> instances = serviceDao.listInstances(jarPackage.getServiceName());

                    for (int i = 0, len = instances.size(); i < len; i++) {
                        Instance instance = instances.get(i);

                        if (instance.getBranch().equals(jarPackage.getBranch())
                                && instance.getVersion().equals(jarPackage.getVersion())) {
                            List<Instance> insItems = groups.get(instance.getMachineId());
                            if (insItems == null) {
                                insItems = new ArrayList<>();
                                groups.put(instance.getMachineId(), insItems);
                            }
                            insItems.add(instance);
                        }
                    }
                    for (Iterator<Map.Entry<String, List<Instance>>> it = groups.entrySet().iterator(); it.hasNext(); ) {
                        Map.Entry<String, List<Instance>> entry = it.next();

                        Machine machine = serviceDao.loadMachine(Long.parseLong(entry.getKey()));
                        //dispatchPackage(jarPackage, machine);
                        List<Instance> insItems = entry.getValue();
                        for (int j = 0, len = insItems.size(); j < len; j++) {
                            //insItems.get()
                        }

                    }
                }
            });

        }
    }

    public boolean dispatchPackage(JarPackage jarPackage, Machine machine) throws IOException {

        StringBuilder url = new StringBuilder("http://")
                .append(machine.getIpAddress())
                .append("/deploy");
        String path = getRealJarPath(jarPackage);
        Map param = new HashMap();
        param.put("path", path.substring(REPOPATH.length()));
        try {
            String ret = HttpClient.uploadFile(url.toString(), new File(path + jarPackage.getJarName()), param);
            return StringUtils.isBlank(ret);
        } catch (Exception e) {
            return false;
        }


    }

    public void commitPackage(JarPackage jarPackage) {
        packageDao.updatePackage(jarPackage);
    }

    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 16000
            , rollbackFor = Exception.class)
    public void deletePackage(String id) {
        long idLong = Long.parseLong(id);
        JarPackage jarPackage = packageDao.loadPackage(Long.parseLong(id));
        File file = new File(getRealJarPath(jarPackage) + jarPackage.getJarName());
        if (file.exists()) {
            file.delete();
        }
        packageDao.deletePackage(idLong);
    }


    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, timeout = 16000
            , rollbackFor = Exception.class)
    public void addPackage(JarPackage jarPackage) {
        jarPackage.setVersion(seqUtils.idStr());
        jarPackage.setId(String.valueOf(seqService.next(SeqService.SeqTypes.JAR)));
        jarPackage.setUpdateTime(new Date());
        packageDao.savePackage(jarPackage);
        MicroService microService = serviceDao.loadService(jarPackage.getServiceName());
        if (microService == null) {
            microService = new MicroService();
            microService.setServiceName(jarPackage.getServiceName());
            serviceDao.saveMicroService(microService);
        }


    }

    public void fixPath(StringBuilder prefixBuf, String[] pathSegs) {

        for (String pathSeg : pathSegs) {
            prefixBuf.append(pathSeg).append(File.separator);
            File file = new File(prefixBuf.toString());
            if (!file.exists()) {
                file.mkdir();
            }

        }
    }

    public String getRealJarPath(JarPackage jarPackage) {
        String[] pathSegs = {jarPackage.getServiceName(), jarPackage.getBranch(), jarPackage.getVersion()};
        StringBuilder prefixBuf = new StringBuilder(PackageService.REPOPATH);
        fixPath(prefixBuf, pathSegs);
        return prefixBuf.toString();
    }

    private String getConfig(JsonNode node, String keyInfo) {
        String[] keys = keyInfo.split("\\.");
        for (String key : keys) {
            node = node.get(key);
            if (node == null) {
                return null;
            }
        }
        return node.asText();
    }

    public JarPackage readPackageInfo(File file) {
        JarPackage jarPackage = null;
        JarFile jarFile = null;
        try {
            jarFile = new JarFile(file);

            Enumeration<JarEntry> jarEntrys = jarFile.entries();
            while (jarEntrys.hasMoreElements()) {
                JarEntry entry = jarEntrys.nextElement();
                String name = entry.getName();
                if (name.endsWith(APPLICATION_YML)) {
                    InputStream in = jarFile.getInputStream(entry);
                    JsonNode node = null;
                    try {
                        node = yamlMapper.readTree(jarFile.getInputStream(entry));
                    } catch (Exception e) {
                        ExceptionUtils.logTrace(logger, e);
                        continue;
                    } finally {
                        in.close();
                    }
                    String serviceName = getConfig(node, SERVICENAME);
                    if (!StringUtils.isBlank(serviceName)) {
                        if (jarPackage == null) {
                            jarPackage = new JarPackage();
                            jarPackage.setJarName(file.getName());
                        }
                        jarPackage.setServiceName(serviceName);
                    }
                    if (jarPackage != null) {
                        String branch = getConfig(node, BRANCH);
                        if (!StringUtils.isBlank(branch)) {
                            jarPackage.setBranch(branch);
                        }
                    }
                }
            }
        } catch (IOException e) {
            ExceptionUtils.logTrace(logger, e);
        } finally {
            try {
                jarFile.close();
            } catch (IOException e) {
                ExceptionUtils.logTrace(logger, e);
            }
        }
        return jarPackage;
    }

    @Transactional(propagation = Propagation.REQUIRED, readOnly = true,
            isolation = Isolation.DEFAULT, timeout = 16000, rollbackFor = Exception.class)
    public Map listRepoViewData(String serviceName, Page page) {
        Map ret = new HashMap<>();
        List<JarPackage> packages = null;
        if(StringUtils.isBlank(serviceName)) {
            packages = packageDao.listPackages( page.getPage(), page.getLimit());
        } else {
            packages = packageDao.listPackagesByServiceName(serviceName, page.getPage(), page.getLimit());
        }
        ret.put("data", packages);
        ret.put("code", 0);
        ret.put("msg", "");
        ret.put("count", packageDao.getPackageCount());
        return ret;
    }

    public static void main(String[] args) {
        File file = new File("D:\\cloud\\esap\\repos\\ability-crm-1.0.jar");
        System.out.println(file.renameTo(new File("D:\\cloud\\esap\\repos\\crm-grouporder-service\\abc" +
                "\\2018-03-14-11-28-07-63875\\ability-crm-1.0.jar")));
        //PackageService p = new PackageService();
        //JarPackage jarPackage = p.readPackageInfo(file);
        //System.out.println(Math.round((double)-/2));
    }

}
